/*
 * @Author: wuqingchun
 * @Date: 2024-10-30 09:31:59
 * @Description: 
 * @LastEditors: wuqingchun
 * @LastEditTime: 2024-11-01 09:08:34
 */
#include <cstdio>
#include <cstring>
#include <cerrno>

#include <unistd.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <arpa/inet.h>

#include "libnet_error.h"
#include "net_connection_base.h"


CNetConnectionBase::CNetConnectionBase(INetEventHandler& refEventHandler, INetTCPConnectionCallback& refCallback, const sockaddr_in& refAddr):
    m_refEventHandler(refEventHandler),
    m_refCallback(refCallback),
    m_stAddr(refAddr)
{
    m_iSockFd = -1;
}

int32_t CNetConnectionBase::InitActive()
{
    m_enType = EnumConnType::CONN_ACTIVE;
    
    return __Connect();
}

int32_t CNetConnectionBase::InitPassive(int iListenerFd)
{
    sockaddr_in stAddr;
    socklen_t uiAddrLen = sizeof(stAddr);

    m_enType = EnumConnType::CONN_PASSIVE;

    int iAcceptFd = accept(iListenerFd, (sockaddr*)&stAddr, &uiAddrLen);
    if (iAcceptFd == -1)
    {
        perror("Failed to accept");
        return ERROR_FAILED;
    }

    m_iSockFd = iAcceptFd;
    m_enState = EnumConnState::CONN_STATE_CONNECTED;
    m_refCallback.OnConnected(this);

    m_refEventHandler.AddRDEvent(this);

    return ERROR_SUCCESS;
}

int CNetConnectionBase::GetFd()
{
    return m_iSockFd;
}

int32_t CNetConnectionBase::DealRDEvent()
{
    printf("Deal readable event, %d, status=%d\n", m_iSockFd, m_enState);
    
    if (m_enState != CONN_STATE_CONNECTED)
    {
        return ERROR_SUCCESS;
    }

    char acBuf[4096];
    ssize_t lRecvLen = 0;

    do
    {
        lRecvLen = __RecvOne(acBuf, sizeof(acBuf));
        if (lRecvLen == -1)
        {
            m_enState = CONN_STATE_DISCONNECTED;
            Close();
        }
        else if (lRecvLen > 0)
        {
            m_refCallback.OnMessage(acBuf, lRecvLen);
        }
    } while (lRecvLen == sizeof(acBuf));

    return ERROR_SUCCESS;
}

int32_t CNetConnectionBase::DealWREvent()
{
    printf("Deal writeable event, %d\n", m_iSockFd);

    if (m_enType == EnumConnType::CONN_ACTIVE && m_enState != EnumConnState::CONN_STATE_CONNECTED)
    {
        m_refEventHandler.DeleteWREvent(this);
    
        int iErr = 0;
        socklen_t uiLen = sizeof(iErr);
        
        if (getsockopt(m_iSockFd, SOL_SOCKET, SO_ERROR, &iErr, &uiLen) < 0 || iErr != 0)
        {
            // 连接失败，需要重试
            printf("Failed to connect server %s:%hu\n", inet_ntoa(m_stAddr.sin_addr), htons(m_stAddr.sin_port));
            //std::this_thread::sleep_for(std::chrono::seconds(1));

            iErr = __Connect();
        }
        else
        {
            m_enState = CONN_STATE_CONNECTED;
            iErr = ERROR_SUCCESS;

            m_refCallback.OnConnected(this);
            m_refEventHandler.AddRDEvent(this);
        }

        return ERROR_SUCCESS;
    }

    // 发送 缓存中的数据
    __SendBuffer();

    return ERROR_SUCCESS;
}

int32_t CNetConnectionBase::DealERREvent()
{
    
}

int32_t CNetConnectionBase::Send(const void* pData, size_t ulDataLen)
{
    ssize_t lSendLen = 0;

    if (m_enState != CONN_STATE_CONNECTED)
    {
        printf("Connection has not been established\n");
        return ERROR_FAILED;
    }

    if (m_objSendBuffer.Empty())
    {
        lSendLen = __SendOne(pData, ulDataLen);
        if (lSendLen == -1)
        {
            // 连接错误
            printf("send error, errno=%d, errstr=%s\n", errno, strerror(errno));

            // 关闭连接
            Close();

            return ERROR_FAILED;
        }
    }
    
    if (lSendLen < ulDataLen)
    {
        printf("Send buffer is full\n");

        if (m_objSendBuffer.AddData((char*)pData + lSendLen, ulDataLen - lSendLen) == 0)
        {
            // 需要关注可写事件
            m_refEventHandler.AddWREvent(this);
        }
        else
        {
            // 数据丢失
            printf("TODO ....\n");
        }

        return ERROR_NOT_COMPLETE;
    }

    return ERROR_SUCCESS;
}


int32_t CNetConnectionBase::__Connect()
{
    if (m_iSockFd != -1)
    {
        close(m_iSockFd);
        m_iSockFd = -1;
    }

    m_iSockFd = socket(AF_INET, SOCK_STREAM|SOCK_NONBLOCK, IPPROTO_TCP);
    if (m_iSockFd == -1)
    {
        printf("socket error, %s\n", strerror(errno));
        return ERROR_FAILED;
    }

    int32_t iErr = connect(m_iSockFd, (const sockaddr*)&m_stAddr, sizeof(m_stAddr));
    if (iErr == -1)
    {
        // connect 系统调用被信号打断之后，不能重试，否则会立即返回错误
        if (errno != EINTR && errno != EINPROGRESS && errno != EAGAIN && errno != EWOULDBLOCK)
        {
            printf("connect error, ret=%d, errno=%d, errstr=%s\n", iErr, errno, strerror(errno));

            return ERROR_FAILED;
        }
    }

    printf("connect, fd=%d, iErr=%d, errno=%d\n", m_iSockFd, iErr, errno);

    iErr = m_refEventHandler.AddWREvent(this);
    if (iErr != ERROR_SUCCESS)
    {
        return iErr;
    }

    m_enState = CONN_STATE_CONNECTING;

    return iErr;
}

ssize_t CNetConnectionBase::__RecvOne(void* pBuf, size_t ulBufLen)
{
    ssize_t lRecv =  0;
    bool bRetry = false;

    do
    {
        lRecv = recv(m_iSockFd, pBuf, ulBufLen, 0);
        if (lRecv == -1)
        {
            if (errno == EINTR)
            {
                // 被信号打断，需要重试
                bRetry = true;
                continue;
            }

            if (errno == EAGAIN || errno == EWOULDBLOCK)
            {
                // 非阻塞模式，没有数据可以读
                lRecv = 0;
                break;
            }
        }
        else if (lRecv == 0)
        {
            // 对端执行了正常的连接关闭
            lRecv = -1;
        }
    } while (bRetry);

    return lRecv;
}


ssize_t CNetConnectionBase::__SendOne(const void* pData, size_t ulDataLen)
{
    bool bRetry = false;
    ssize_t lSendLen  = 0;

    do
    {
        lSendLen = send(m_iSockFd, pData, ulDataLen, MSG_NOSIGNAL);
        if (lSendLen == -1)
        {
            if (errno == EINTR)
            {
                // 被信号中断，重试
                bRetry = true;
                continue;
            }

            if (errno == EAGAIN || errno == EWOULDBLOCK)
            {
                lSendLen = 0;
            }
        }
    } while (bRetry);

    return lSendLen;
}

void CNetConnectionBase::__SendBuffer()
{
    BUFFER_ITEM_S stItem;
    ssize_t lSendLen = 0;
    bool bDeleteWEvent = true;

    while (m_objSendBuffer.GetItem(stItem))
    {
        lSendLen = __SendOne(stItem.pcData, stItem.ulDataLen);
        if (lSendLen == -1)
        {
            m_objSendBuffer.PushBack(stItem);
            bDeleteWEvent = false;
            break;
        }

        if (lSendLen < stItem.ulDataLen)
        {
            stItem.pcData = stItem.pcData + lSendLen;
            stItem.ulDataLen -= lSendLen;
            m_objSendBuffer.PushBack(stItem);
            bDeleteWEvent = false;
            break;
        }

        m_objSendBuffer.Free(stItem);
    }

    if (bDeleteWEvent)
    {
        m_refEventHandler.DeleteWREvent(this);
    }
}

void CNetConnectionBase::Close()
{
    if (m_iSockFd == -1)
    {
        return;
    }

    printf("Connection %p:%d closed\n", this, m_iSockFd);
    
    // 从事件管理对象中删除描述符
    m_refEventHandler.DeleteRDEvent(this);
    m_refEventHandler.DeleteWREvent(this);

    (void)close(m_iSockFd);
    m_iSockFd = -1;
    m_enState = CONN_STATE_DISCONNECTED;
    m_refCallback.OnDisconnected(this);

    if (m_enType == CONN_ACTIVE)
    {
        // 发起重连

        __Connect();
    }
}